const path = require("path");
const fs = require('fs');

/**
 * Get diff files
 * @param baseDir Origin project directory path
 * @returns {Promise<Array<string>>}
 */
async function diffCode(baseDir = process.cwd()) {
  const simpleGit = require('simple-git');
  const options = {
    baseDir,
    binary: 'git',
    maxConcurrentProcesses: 6,
    trimmed: false,
  };
  const git = simpleGit(options);
  // await git.clean(simpleGit.CleanOptions.FORCE);
  // await git.reset(['--hard']);
  await git.fetch()
  return new Promise((resolve) => {
    git.diffSummary(['dev', 'origin/dev'], (err, update) => {
      if (err) {
        console.error('Error during git diff:', err);
        resolve([]);
      }
      if (update && update.files && update.files.length > 0) {
        // console.log('Modified files:');
        // console.log(update.files)
        resolve(update.files);
      } else {
        console.log('No files modified.');
        resolve([]);
      }
    })
  })
}

/**
 * Update code use git pull
 * @param baseDir Origin project directory path
 * @param modifiedFiles
 * @returns {Promise<Array<string>>}
 */
async function updateCode(baseDir = process.cwd(), modifiedFiles = []) {
  const simpleGit = require('simple-git');
  const options = {
    baseDir,
    binary: 'git',
    maxConcurrentProcesses: 6,
    trimmed: false,
  };
  const git = simpleGit(options);
  // await git.clean(simpleGit.CleanOptions.FORCE);
  // await git.reset(['--hard']);
  return new Promise((resolve, reject) => {
    git.pull('origin', 'dev', ['--stat'], (err, update) => {
      if (err) {
        console.error('Error during git pull:', err);
        reject([]);
      } else resolve(modifiedFiles)
    });
  })
}

/**
 * Replace code from origin to target project directory
 * @param baseDir Origin project directory path
 * @param targetDir Target project directory path
 * @param changeFiles Files to replace
 * @returns {Promise<boolean>}
 */
async function replaceCode(baseDir, targetDir, changeFiles) {
  const {promisify} = require('util');
  const readFile = promisify(fs.readFile);
  const writeFile = promisify(fs.writeFile);
  console.log(changeFiles)
  let batchSuccessFlag = true;
  for (let file of changeFiles) {
    if (file.file.includes('package.json') || file.file.includes('request.js')) {
      console.log("Protected files detected, skip...");
    } else {
      const originPath = path.join(baseDir, file.file);
      const targetPath = path.join(targetDir, file.file);
      try {
        const fileData = await readFile(originPath);
        await writeFile(targetPath, fileData);
        console.log(`Replace file success: ${targetPath}`);
      } catch (error) {
        console.error('Replace file failed:', error);
        batchSuccessFlag = false;
      }
    }
  }
  return batchSuccessFlag;
}

/**
 * Run shell commands
 * @param command
 * @param args
 * @param options
 * @returns {Promise<unknown>}
 */
function runCommand(command, args, options) {
  const {spawn} = require('child_process');
  return new Promise((resolve, reject) => {
    const spawnedProcess = spawn(command, args, options);
    spawnedProcess.stdout.on('data', (data) => {
      process.stdout.write(data);
    });
    spawnedProcess.stderr.on('data', (data) => {
      process.stderr.write(data);
    });
    spawnedProcess.on('close', (code) => {
      if (code === 0) {
        resolve(true);
      } else {
        reject(new Error(`Command failed with exit code ${code}`));
      }
    });
  });
}

/**
 * Execute build commands
 * @param targetDir
 * @returns {Promise<boolean>}
 */
async function build(targetDir) {
  try {
    await runCommand('yarn', [], {cwd: targetDir, shell: true});
    await runCommand('yarn', ['build:pro'], {cwd: targetDir, shell: true});
    return true;
  } catch (error) {
    console.error('An error occurred:', error);
    return false;
  }
}

/**
 * Zip dist folder to system tmp
 * @param distDir yarn build dist
 * @returns {Promise<string>}
 */
async function makeZip(distDir) {
  const os = require('os');
  const tmpDir = os.tmpdir();
  const archiver = require('archiver');
  const fileName = `techdev-${new Date().getTime()}.zip`;
  const distName = path.join(tmpDir, fileName);
  const output = fs.createWriteStream(distName);
  const archive = archiver('zip', {
    zlib: {level: 9}
  });
  return new Promise((resolve, reject) => {
    archive
      .directory(distDir, false)
      .on('error', err => {
        console.error(err)
        reject(new Error("Package zip failed"))
      })
      .pipe(output);
    output.on('close', () => resolve(fileName));
    archive.finalize();
  });
}

module.exports = {
  diffCode,
  updateCode,
  replaceCode,
  build,
  makeZip
}
